package com.dotflat.rest.service.impl;

import com.dotflat.rest.PO.AreableObjectBasePO;
import com.dotflat.rest.domain.AreableObjectBase;
import com.dotflat.rest.service.AreableObjectBaseService;
import com.dotflat.rest.utils.LikeableField;
import com.dotflat.rest.utils.ReadonlyField;
import com.starinside.wp.foundation.exception.BusinessException;
import com.starinside.wp.foundation.model.ErrorCode;

import com.matt.service.care.HLDailyRecordService;
import com.starinside.wp.service.ChildrenService;
import com.tuo51.foudation.utils.CopyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;


@Service
public class AreableObjectBaseServiceImpl<T1 extends AreableObjectBase, T2 extends JpaSpecificationExecutor<T1> & JpaRepository<T1,String>>
        implements AreableObjectBaseService<T1> {

    protected T2 baseRepository;
    public void setBaseRepository(T2 baseRepository) {
        this.baseRepository = baseRepository;
    }

    @Autowired
    ChildrenService childrenService;

    @Autowired
    HLDailyRecordService dailyRecordService;

    private boolean permissionCheck(T1 object, String areaId){
        if (object == null || StringUtils.isEmpty(areaId)){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "对象不存在");
        }

        String targetAreaId = object.getAreaId();
        if (StringUtils.isEmpty(object.getAreaId())){
            //公共对象
            return true;
        }
        if (StringUtils.isEmpty(targetAreaId)
                || !areaId.equals(targetAreaId)){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "对象不存在!");
        }

        return true;
    }

    @Override
    public T1 get(String areaId, String staffId, String id) {
        T1 t = baseRepository.getOne(id);

        permissionCheck(t, areaId);

        return t;
    }

    @Override
    public Page<T1> page(String areaId, String staffId, AreableObjectBasePO param, PageRequest pageRequest) {
//
//        param.setCreatedBy(staffId);
        param.setAreaId(areaId);
        param.setDeleted(false);

        Specification<T1> specification = new Specification<T1>() {
            @Override
            public Predicate toPredicate(Root<T1> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();

                //获取最新数据，解决部分更新时jpa其他字段设置null问题
                try {

                    //反射获取Class的属性（Field表示类中的成员变量）
                    Class clazz = null;
                    do{
                        if (clazz == null){
                            clazz = param.getClass();
                        }else{
                            clazz = clazz.getSuperclass();
                        }

                        for (Field field : clazz.getDeclaredFields()) {

                            //获取授权
                            field.setAccessible(true);

                            //属性名称
                            String fieldName = field.getName();

                            //属性的值
                            Object fieldValue = field.get(param);

                            //找出值为空的属性，值为空则为忽略属性，或者被NotFound标注，我们复制的时候不进行赋值
                            if(fieldValue != null){
                                if (fieldValue instanceof String && StringUtils.isEmpty((String)fieldValue)){
                                    continue;
                                }

                                if (field.isAnnotationPresent(LikeableField.class)){
                                    //模糊查找
                                    Predicate p = criteriaBuilder.like(root.get(fieldName), "%" + fieldValue + "%");
                                    predicates.add(p);
                                }else if("areaId".equals(fieldName)) {
                                    //areaId 为空时也需要查出来
                                    Predicate p1 = criteriaBuilder.equal(root.get(fieldName), fieldValue);
                                    Predicate p2 = criteriaBuilder.isNull(root.get(fieldName));
                                    predicates.add(criteriaBuilder.or(p1, p2));
                                }else{
                                    //精确
                                    Predicate p = criteriaBuilder.equal(root.get(fieldName), fieldValue);
                                    predicates.add(p);
                                }
                            }
                        }
                    }while (clazz != AreableObjectBasePO.class && clazz != Object.class && clazz != null);

                } catch (IllegalAccessException e) {
                    //输出到日志文件中
                    e.printStackTrace();
                }

                query.where(predicates.toArray(new Predicate[predicates.size()]));
                return query.getRestriction();
            }
        };

        Page<T1> page = baseRepository.findAll(specification, pageRequest);

        return page;
    }

    @Override
    public void delete(String areaId, String staffId, AreableObjectBasePO param) {

        if (StringUtils.isEmpty(param.getId())){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "对象不存在");
        }

        T1 t = get(areaId, staffId, param.getId());

        if (t == null){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "记录不存在");
        }

        if (t.isOwnerOnly() && !t.getCreatedBy().equals(staffId)){
            throw new BusinessException(ErrorCode.PERMISSION_DENIED, "没有操作权限");
        }

        t.setDeleted(true);

        baseRepository.save(t);
    }

    @Override
    public void delete(String areaId, String staffId, String[] ids){
        if (ids == null || ids.length == 0){
            throw new BusinessException(ErrorCode.PARAM_CAN_NOT_BLANK, "参数错误");
        }

        List<T1> items = baseRepository.findAllById(Arrays.asList(ids));
        if (items.size() != ids.length){
            throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "对象不存在");
        }

        for (T1 t: items){
            if (!t.getAreaId().equals(areaId)){
                throw new BusinessException(ErrorCode.MATERIAL_NOT_EXIST, "对象不存在!");
            }

            if (t.isOwnerOnly() && !t.getCreatedBy().equals(staffId)){
                throw new BusinessException(ErrorCode.PERMISSION_DENIED, "没有操作权限");
            }

            t.setDeleted(true);
        }

        baseRepository.saveAll(items);
    }


    @Override
    public T1 add(String areaId, String staffId, AreableObjectBasePO param) {
        return add(areaId, staffId, param, null);
    }

    @Override
    public T1 add(String areaId, String staffId, AreableObjectBasePO param, Consumer<? super T1> preAction) {
        ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();
        Class clazz = (Class<T1>) ptype.getActualTypeArguments()[0];

        try {
            T1 o = null;
            if (StringUtils.isEmpty(param.getId())){
                o = (T1) clazz.newInstance();
            } else {
                o = get(areaId, staffId, param.getId());
            }

            //找到只读的字段
            List<Field> fields = FieldUtils.getFieldsListWithAnnotation(param.getClass(), ReadonlyField.class);
            if (fields.size() > 0){
                List<String> fieldNames = fields.stream().map(f->f.getName()).collect(Collectors.toList());
                fieldNames.add("id");
                fieldNames.add("areaId");
                String[] fieldArr = (String[]) fieldNames.toArray(new String[fieldNames.size()]);

                CopyUtils.copyPropertiesSkipNullAndExternalProperties(param, o, fieldArr);
            }else{
                CopyUtils.copyPropertiesSkipNullAndExternalProperties(param, o, new String[]{
                        "id", "areaId"
                });
//                BeanUtils.copyProperties(param, o, "id", "areaId");
            }


            if (preAction != null) {
                preAction.accept(o);
            }

            o.setDeleted(false);
            o.setAreaId(areaId);
            o.setCreated(new Date());
            o.setCreatedBy(staffId);

            baseRepository.save(o);

            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        throw new BusinessException(ErrorCode.UNDEFINED, "模板对象错误");
    }
}
